home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 25 / CU Amiga Magazine's Super CD-ROM 25 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-08].iso / CUCD / Programming / PPCpack / c.txt next >
Text File  |  1998-05-26  |  18KB  |  507 lines

  1. B) PowerPC Support in C or C++
  2. ==============================
  3.  
  4. Principially PPC Developpement in C/C++ runs in 5 phases:
  5.  
  6. Note: If you are using vbcc-WarpOS, and not StormC, then you should
  7. also read vbcc.doc !!! Coding with vbcc differs in some parts to
  8. coding with StormC !!!
  9.  
  10. 1) Rewrite all 68k ASM Stuff in C
  11. 2) Adapt Source to ANSI/StormC
  12. 3) Adapt to PPC
  13. 4) Contextswitch-Optimizing
  14. 5) Further Adaptions
  15.  
  16. Contrary to what you might believe, 3) is only a very small step,
  17. the big step is 2). And yes, you can do this already, even if you
  18. do not own a PPC, mainly. I will explain the different steps of
  19. developpement now in a more detailed way.
  20.  
  21. It has to be outlined, that it is advised to do steps 1)/2) already
  22. while developping an 68k version, even if at first no PPC Version is
  23. planned. It will simplify the PPC Developpement much, and it in fact
  24. does not need too much extra work...
  25.  
  26. It has also to be noted, that things are not that easy using the PPC
  27. Software from Phase 5. This is a special feature of the WarpOS Software,
  28. that things can be such easy.
  29.  
  30. I won't discuss rewriting 68k ASM to C Source here, you should be able
  31. to do this yourselves.
  32.  
  33. 2) Adapt Source to ANSI/StormC
  34. ------------------------------
  35.  
  36. The most work is not the adaption to PPC, but the adaption from SAS/C or
  37. GNU C to StormC. StormC is a strict ANSI compiler, because of that it
  38. knows only Standard-C-Functions that are contained in the ANSI-Standard.
  39. Some of the not-supported functions can be emulated using the not-yet-released
  40. UnixLib, though.
  41.  
  42. It should be noted, that, if your program compiles on SAS/C with the
  43. STRICT ANSI mode set. You can think of StormC as a compiler that ALWAYS
  44. runs in STRICT ANSI mode.
  45.  
  46. The following SAS/C Functions are not contained in ANSI, and thus not
  47. supported by StormC (most of them are quite exotic functions, and it is
  48. possible that you do not even know a lot of them, even if you are a proficient
  49. C Coder) :
  50.  
  51. astcsma     isascii     iscsym      iscsymf     toascii     scdir       stcpm
  52. stcpma      stcsma      stccpy      stpcpy      stcis       stcisn      stclen
  53. stpbrk      stpchr      stpchrn     strcmpi     strnset
  54. strset      stcarg      stpsym      stptok      stpblk      strbpl      strdup
  55. strins      strmid      stcd_i      stcd_l      ecvt        fcvt        gcvt
  56. stch_i      stch_l      stci_d      stci_h      stci_o      stcl_d      stcl_h
  57. stcl_o      stco_i      stco_l      stcu_d      stcul_d     toascii     stpdate
  58. stptime     __datecvt   __timecvt   utpack      utunpk      cot         iabs
  59. max         min         pow2        __emit      getreg      putreg      geta
  60. isatty      ovlyMgr     dqsort      fqsort      lqsort      sqsort      strsrt
  61. tqsort      drand48     erand48     jrand8      lcong48     lrand48     mrand8
  62. nrand48     seed48      srand48     __autoopenfail          chkabort    Chk_Abort
  63. _CXBRK      __exit      onexit      _XCEXIT     forkl       forkv       onbreak
  64. wait        waitm       bldmem      rstmem      sizmem      chkml       getmem
  65. getml       halloc      lsbrk       sbrk        _MemCleanup rbrk        rlsmem
  66. rlsml       memccpy     movmem      repmem      setmem      swmem       except
  67. __matherr   poserr      datecmp     timer       __tzset     getch       fgetchar
  68. fputchar    _dread      _dwrite     read        write       clrerr      close
  69. _dclose     fcloseall   creat       _dcreat     _dcreatx    fdopen      fileno
  70. fmode       iomode      open        _dopen      flushall    mkstemp     mktemp
  71. setnbf      _dseek      lseek       tell        access      chkufb      chmod
  72. fstat       getfa       getft       stat        stcgfe      stcgfn      stcgfp
  73. strmfe      strmfn      strmfp      strsfn      unlink      argopt      chgclk
  74. dos_packet  getclk      getasn      getdfs      putenv      rawcon      stackavail
  75. stacksize   stackused   chdir       closedir    dfind       dnext       findpath
  76. getcd       getcwd      getfnl      getpath     mkdir       opendir     readdir
  77. rmdir       seekdir     rewinddir   telldir     readlocale  scr_beep    scr_bs
  78. scr_cdelete scr_cinsert scr_clear   scr_cr      scr_curs    scr_cursrt  scr_cursup
  79. scr_eol     scl_home    scr_ldelete scr_lf      scr_linsert scr_tab     _CXFERR
  80. _CXOVF      _EPILOG     _PROLOG
  81.  
  82. The most important of the "not allowed" functions are the Level 0 I/O functions
  83. (open,close,read,write). Use fopen,fclose,fread,fwrite instead.
  84.  
  85. Note: Some of these functions might be included, in the first version of this text
  86. i by mistake declared stricmp and strnicmp as not included (what is wrong), there
  87. might be more errors in the list :) But probably not many... probably none...
  88.  
  89. But STRICT ANSI does not only limit the functions, there are also some things,
  90. that cause a warning from SAS/C, but an error from a strict ANSI Compiler.
  91.  
  92. Things like:
  93.  
  94. char *string=malloc(300);
  95.  
  96. cause an error from StormC. Correct would be:
  97.  
  98. char *string=(char *)malloc(300);
  99.  
  100. ANSI wants STRONG TYPING. If you do not own StormC, but want to make your code
  101. as easy compilable with StormC PPC later, compile with STRICT ANSI. Problems
  102. appear especially with function pointers. If you are not sure how to cast
  103. a thing for STRICT ANSI, maybe you should try void *, it works often for
  104. not strongly typed source.
  105.  
  106. You should also replace all K&R Syntax (example)
  107.  
  108. void main(argv,argc)
  109. int argv;
  110. char **argc;
  111.  
  112. by the normal syntax (example)
  113.  
  114. void main(int argv,char **argc);
  115.  
  116. Also a code like
  117.  
  118. int a=5;
  119. int stuff[a];
  120.  
  121. is not legal on ANSI. Array Dimensions have to be constants.
  122. If you need them variable, use dynamic allocation using malloc.
  123.  
  124. A good method to convert to "Strict ANSI" is the following:
  125.  
  126. 1. Just compile it, and look at every warning and error
  127. 2. Typecast everything that looks like a pointer (and causes
  128.    an error) to void *, everything else that causes problems,
  129.    to a int, long or double.
  130. 3. If some things still don't work, have a look at them now.
  131.  
  132. Some Sources (like the Source of Doom) require parts of the
  133. Unix/TCP includes. If you need such things, please contact me,
  134. i have converted the needed things to StormC (contact address
  135. see below).
  136.  
  137. Now we are nearly done with the ANSI/StormC Adaption. At the end
  138. some keyword have to be defined differently:
  139.  
  140. #define __stdargs
  141. #define __regargs
  142. #define __asm
  143. #define __far FAR
  144. #define __inline inline
  145. #define __volatile volatile
  146.  
  147. __chip, __fast and __interrupt do not exist on StormC, they have to
  148. be replaced by the appropriate OS Functions. Some programmers also
  149. use some strange cominations that won't work (static inline is complete
  150. nonsese, get it Unix-coders :) !!! Static OR inline but not both of them !!!)
  151.  
  152. And if we are at "bad coding style": Bitfields only exist on C++, not
  153. in ANSI C...
  154.  
  155. Ah, and one word to those fclose-always-works-fans. No, fclose does
  156. not work, if the file is NOT OPEN !!! You crash your task, if you try
  157. to close a file, that is not open.
  158.  
  159. Do
  160.  
  161. if (file) fclose(file);
  162.  
  163. Some words to __attribute__ ((packed)). It does not exist, and is a
  164. feature that would slow down the PPC *much*, if it would exist. Please
  165. do not use __attribute ((packed)). The PPC needs a certain alignment
  166. to get optimal speed.
  167.  
  168. About Text Constants longer than a line:
  169.  
  170. It is legal to write:
  171.  
  172. char *bla="...."\
  173.           "...."\
  174.           "....";
  175.  
  176. But the last character before the \ should be a \ here.
  177.  
  178. The notation
  179.  
  180. char bla[]={"..."\
  181.             "..."};
  182.  
  183. is not legal (This is sometimes used in GNU C Sources).
  184.  
  185. If you have done all this, you now (should) have a working StormC 68k Source.
  186. Now we go to the PPC stuff. The most work is done now. Only small things
  187. remain to do. PPC-handling is mostly done internal by the compiler.
  188.  
  189. C. Adapt to PPC
  190. ---------------
  191.  
  192. At first we have to change register parameters:
  193.  
  194. void test(register __a0 mytest);
  195.  
  196. has to be changed (for example) to
  197.  
  198. void test(register mytest);
  199.  
  200. The PPC does not know a register a0. But you can tell him to use a
  201. register by usage of the keyword "register", without specifying a
  202. register number.
  203.  
  204. Next we have to do some changes to OS-Includes:
  205.  
  206. up to now, depending on which compiler you used, you did (example):
  207.  
  208. #include <clib/exec_protos.h>
  209. #include <pragmas/exec_pragmas.h>
  210.  
  211. or
  212.  
  213. #include <clib/exec_protos.h>
  214. #include <pragma/exec_lib.h>
  215.  
  216. or
  217.  
  218. #include <clib/exec_protos.h>
  219. #include <inline/exec.h>
  220.  
  221. or
  222.  
  223. #include <proto/exec.h>
  224.  
  225. For StormC PPC you do:
  226.  
  227. #include <clib/exec_protos.h>
  228.  
  229. Do not include any pragmas/pragma files, or you will be swamped by error-messages.
  230. Also do not include any proto/ files.
  231.  
  232. If you want to compile your source for both 68k and PPC (without changing the
  233. source) you do:
  234.  
  235. #include <clib/exec_protos.h>
  236. #ifndef __PPC__
  237. #include <pragma/exec_lib.h>
  238. #endif
  239.  
  240. __PPC__ is always set correctly.
  241.  
  242. Yet another difference between 68k and PPC concerns the usage of Subtasks. If you
  243. want to do the Subtask as PPC Task (recommended) you have to replace functions like
  244. CreateTask() by CreateTaskPPC() of the powerpc.library. I won't go into detail here,
  245. most of the time the API is absolutely identic to the usual functions, with the
  246. exception of a PPC at the end of the function name. Read the documentation of
  247. WarpOS for more information.
  248.  
  249. The other method would be doing the subtask as 68k task and calling CreateTask().
  250. To do so you would have to make your program a mixed Binary, though, and you also
  251. would not get full PPC Speedup. So usually (unless the subtask does many OS Calls)
  252. the CreateTaskPPC() approach is the better method. Also, it is recommended not to
  253. use 68k Subtasks in PPC programs, so that your program will get optimal speed
  254. on a 100% PPC Amiga System (that surely will appear some time in the future).
  255.  
  256. Earlier versions of the compiler had problems with Tags-versions of OS-functions.
  257. This is fixed since quite some time now. I did not notice, that is why i said
  258. in earlier versions of this document, that you would have to change this code.
  259. I did not test since quite some time.
  260.  
  261. Then we come to the BeginIO-Function. This function only exists with a
  262. Library Base on the PPC Compiler. You can use the following code (example
  263. is for audio.device):
  264.  
  265. #include <libraries/powerpc.h>
  266. #include <ppcamiga.h>
  267.  
  268. void BeginIOAudioPPC(struct IORequest *arg1)
  269. {
  270.         extern struct Library *AudioBase;
  271.         ULONG regs[16];
  272.         regs[9] = (ULONG) arg1;
  273.         __CallLibrary(AudioBase,-30,regs);
  274. }
  275.  
  276. An example how this can be used (out of the Sound-Code of ZhaDoom...):
  277.  
  278.   AudioBase = (struct Library *)audio_io->ioa_Request.io_Device;
  279.   c = &channel_info[cnum];
  280.   c->audio_io->ioa_Request.io_Command = CMD_WRITE;
  281.   c->audio_io->ioa_Request.io_Flags = ADIOF_PERVOL;
  282.   c->audio_io->ioa_Data = &chip_cache_info[cache_chip_data (id)].chip_data[8];
  283.   c->audio_io->ioa_Length = lengths[id] - 8;
  284.   c->audio_io->ioa_Period = period_table[pitch];
  285.   c->audio_io->ioa_Volume = vol << 2;
  286.   c->audio_io->ioa_Cycles = 1;
  287. #ifdef __PPC__
  288.   BeginIOAudioPPC((struct IORequest *)c->audio_io);
  289. #else
  290.   BeginIO ((struct IORequest *)c->audio_io);
  291. #endif
  292.  
  293. You see? You always have to read out the LibraryBase of a device to do
  294. a BeginIO on PPC...
  295.  
  296. Some readers now probably ask themselves what about the famous
  297. "Context-Switch". Well, the truth is, under StormC, the Compiler
  298. automatically deals with the Contextswitch. You won't have to think
  299. about it... i will lose some words about it anyways:
  300.  
  301. There are two sorts of Contextswitches:
  302.  
  303. a) Function-Contextswitches
  304.  
  305. You have to compile with Debugging-Information the first time you compile
  306. the Source. Then the compiler handles the Contextswitches automatically.
  307. Later you can compile without Debugging-Information, if you want.
  308.  
  309. b) Library-Contextswitches
  310.  
  311. These need so-called "function-stubs". ppcamiga.lib already contains
  312. the function-stubs for all Amiga-OS-functions, and for the 68k-functions
  313. of rtgmaster (But for rtgmaster also PPC-functions exist, and it is
  314. adviced to use these). To create a stub for a not yet supported library,
  315. you do:
  316.  
  317. genppcstub mylib_protos.h mylib.fd VERBOSE
  318.  
  319. You need the proto- and the FD-File to create the stub. The stub is a
  320. C Source file that you link together with your Source. The Contextswitch
  321. itselves then works automatically.
  322.  
  323. D.) Contextswitch-Optimizing
  324. ----------------------------
  325.  
  326. With WarpOS a Contextswitch needs about 0.5 milliseconds (with a 200 MHz
  327. PPC 604e Board...). It should be avoided to do "many Contextswitches
  328. per Second" (BTW: The Phase 5 Software needs about 1 millisecond for a
  329. Contextswitch).
  330.  
  331. Example of things to avoid:
  332.  
  333. - Load Files on a Byte-per-Byte basis with fgetc (use fread instead
  334.   and load to a Fastram Buffer, from which you get the stuff on a
  335.   Byte-Per-Byte-Basis then)
  336. - WritePixel (work on a Fastram-Buffer instead)
  337. - OS-Calls that are called often per second
  338.  
  339. Graphics can be handled completely PPC Native by using rtgmaster.
  340. rtgmaster is a PPC Shared Library.
  341.  
  342. Notice, that some of the Standard-C-Functions do Contextswitches.
  343. I think clock() is among them, but am not sure about it. A possibility
  344. to deal timing without Contextswitches for sure is to use the PPC
  345. timer directly, in PPC ASM:
  346.  
  347. double tb_scale_lo = ((double)(bus_clock >> 2)) / 35.0;
  348. double tb_scale_hi = (4.294967296E9 / (double)(bus_clock >> 2)) * 35.0;
  349.  
  350. bus_clock is set to the Bus Clock in Hz, for example 50000000 for
  351. a 150 MHz Board, 66000000 for a 200 MHz Board.
  352.  
  353. Stopping time is then done like this (example of the I_GetTime-function
  354. of Doom):
  355.  
  356. int I_GetTime (void)
  357. {
  358.  unsigned int clock[2];
  359.  double currtics;
  360.  static double basetics=0.0;
  361.         ppctimer (clock);
  362.  if (basetics == 0.0)
  363.   basetics = ((double) clock[0])*tb_scale_hi + ((double) clock[1])/tb_scale_lo;
  364.  currtics = ((double) clock[0])*tb_scale_hi + ((double) clock[1])/tb_scale_lo;
  365.  return (int) (currtics-basetics);
  366. }
  367.  
  368.  
  369. ppctimer looks like (object code for people who do not have StormPowerASM
  370. is contained inside this archive):
  371.  
  372.  vea
  373.  XDEF    _ppctimer
  374.  
  375. _ppctimer:      mftbu   r4
  376.                 mftbl   r5
  377.                 mftbu   r6
  378.                 cmpw    r4,r6
  379.                 bne     _ppctimer
  380.  
  381.                 stw     r4,0(r3)
  382.                 stw     r5,4(r3)
  383.                 blr
  384.  
  385. But well, as i said, i am not sure, if clock() does use Contextswitches or not.
  386. Only i had the feeling that ZhaDoom speed up, after i replaced the usage of clock()
  387. by the usage of ppctimer().
  388.  
  389. 5) Further Adaptions
  390. --------------------
  391.  
  392. Note: The following is fully optional !!! (But it might speed up some things)
  393.  
  394. It is possible to declare waste memory-areas as non-cachable using the BAT-registers
  395. of the PPC. How this is exactly done, read the documentation of WarpOS.
  396.  
  397. Another optimization would be re-writing parts of the code in PPC Assembler.
  398. As to this, see below.
  399.  
  400. In some newsgroups it was discussed to run program parts asynchronely on the
  401. 68k. Some people even claimed this would only be possible with the Phase 5
  402. software. This is not true, if you want to implement it, you would use the
  403. PPC-Native Message-System of WarpOS (keyword "AllocXMsg", refer to WarpOS
  404. documentation). But i want to outline the disadvantages of this "parallel"
  405. method:
  406.  
  407. 1) On PPC-only machines such code would have serious disadvantages. And such
  408.    systems will come...
  409. 2) The PowerUP-Hardware is not good for true Multi-Processoring. As soon as
  410.    your 68k/PPC tasks share memory, you will get serious problems. I won't get
  411.    into detail, it was discussed enough in the newsgroup. And it really is not
  412.    worth the effort.
  413.  
  414. I seriously recommend to work only "synchrone", doing Sub-Tasks only on the
  415. same CPU the mainprogram also is running on.
  416.  
  417. Sometimes it is also useful to do a manual Contextswitch to a 68k ASM function.
  418. If the ASM functions contains tons of OS calls, for example. But if you have such
  419. code, i recommend using a Mixed Binary, anyways. Makes things more easy.
  420.  
  421. PowerPC ASM Optimization
  422. ------------------------
  423.  
  424. At last this one. Again i have to say, that it makes no sense to implement
  425. the whole stuff in PPC ASM. You start like this:
  426.  
  427. 1) Implement all in C
  428. 2) Compile it for 68k and use the Profiler of StormC (the profiler currently
  429.    only exists for 68k, but its data is also useful for PPC)
  430.  
  431. When you use the profiler you run the program, and it does a statistic about
  432. which functions use how much CPU time. Then you implement the functions that
  433. take the most CPU time in PPC ASM. It is that simple.
  434.  
  435. You have to keep in mind, though:
  436.  
  437. - even ASM can't speedup massive numbers of Context-Switches
  438. - ASM also can't speed up the slow GFX Bus of the Amiga (Even Zorro III is
  439.   slow as to today's standards...)
  440.  
  441. Remember always:
  442.  
  443. Doing a fast implementation in C and then using a Profiler to find out which
  444. functions are worth a ASM Optimization is much more clever than doing everything
  445. in PPC ASM.
  446.  
  447. Of course the profiler is only available, if you own StormC. SAS/C and GNU C
  448. do not have a profiler.
  449.  
  450. Now, what do you do, if your "original" source is in ASM, not in C ? Well,
  451. you insert timing checks and write some timing data to a file ("manual
  452. Profiling") at places where you think the most time is wasted. Of course,
  453. real profiling (using StormC) is much more easy. Also remember, that C
  454. defines it's functions like:
  455.  
  456. _Functionname
  457.  
  458. So if you want to profile ASM-Stuff you have to add a leading _ to all functionnames,
  459. and to XDEF them all.
  460.  
  461. Example:
  462.  
  463. stuff.asm
  464. ---------
  465.  
  466. start:
  467.  
  468. jsr morestuff
  469. ; lots of code
  470.  
  471. rts
  472. ; lots of functions
  473. morestuff:
  474. ; lots of code
  475. rts
  476.  
  477. Would have to be changed to:
  478.  
  479. startit.c
  480. ---------
  481.  
  482. extern void start(void);
  483.  
  484. void main()
  485. {
  486.  start();
  487. }
  488.  
  489. stuff.asm
  490. ---------
  491.  
  492.     XDEF _start
  493.     XDEF _morestuff
  494.     ;... lots of functions
  495. _start:
  496.     jsr _morestuff
  497.     ;lots of code
  498.  
  499.     rts
  500. _morestuff:
  501.     ;lots of code
  502.     rts
  503.  
  504. Well, and now you can start profiling... the C thing simply starts the ASM
  505. main function...
  506.  
  507.